package com.zaoke.web.service.order.impl;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.lang.time.DateUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.zaoke.web.bo.food.Food;
import com.zaoke.web.bo.food.FoodPrice;
import com.zaoke.web.bo.order.Order;
import com.zaoke.web.bo.order.OrderFood;
import com.zaoke.web.bo.order.OrderQuery;
import com.zaoke.web.bo.order.OrderQueryRes;
import com.zaoke.web.bo.order.PayMode;
import com.zaoke.web.bo.user.User;
import com.zaoke.web.bo.zone.Zone;
import com.zaoke.web.dao.order.OrderDao;
import com.zaoke.web.dao.order.OrderFoodDao;
import com.zaoke.web.dao.zone.ZoneDao;
import com.zaoke.web.service.order.OrderService;

public class OrderServiceImpl implements OrderService{
	
	private Log logger = LogFactory.getLog(getClass());

	private OrderDao orderDao;
	
	private OrderFoodDao orderFoodDao;
	
	private ZoneDao zoneDao;
	
	private boolean upgrade;

	public void setOrderDao(OrderDao orderDao) {
		this.orderDao = orderDao;
	}
	
	public void setOrderFoodDao(OrderFoodDao orderFoodDao) {
		this.orderFoodDao = orderFoodDao;
	}

	public void setZoneDao(ZoneDao zoneDao) {
		this.zoneDao = zoneDao;
	}

	@Override
	public List<OrderQueryRes> queryOrderInfo(OrderQuery query) {
		
		if(query==null)
			return new ArrayList<OrderQueryRes>();
		
		List<OrderQueryRes> res = new ArrayList<OrderQueryRes>();
		Order order = new Order();
		order.setPayway(query.getPayway());
		order.setUserid(query.getUserid());
		order.setZoneid(query.getZoneid());
		order.setState(query.getState());
		//order.setConsumetime(query.getPickdate());
		
		Date dateF = null;
		Date dateT = null;
		if(query.getPickdateF() != null)
			dateF = DateUtils.truncate(query.getPickdateF(), Calendar.DAY_OF_MONTH);
		if(query.getPickdateT() != null)
			dateT = DateUtils.ceiling(query.getPickdateT(), Calendar.DAY_OF_MONTH);
		
		List<Order> orders = orderDao.getOrderByQuery(order, dateF, dateT);
		Map<Long,List<OrderFood>> orderfoodMap = new HashMap<Long, List<OrderFood>>();
		if(orders != null)
			for(Order o : orders){
				List<OrderFood> or = orderFoodDao.getOrderFoodsByOrderid(o.getOrderid());
				o.setOrderFood(or);
				orderfoodMap.put(o.getOrderid(), or);
			}
		
		if(orders != null && orders.size() > 0 && upgrade){
			logger.debug("getOrderByQuery >> " + orders);
			long start = System.currentTimeMillis();
			Map<Date, List<Order>> groupOrders = new HashMap<Date, List<Order>>();
			List<User> users = new ArrayList<User>();
			Map<Date, Date> minOrderTime = new HashMap<Date, Date>();
			Map<Date, Date> consumeTime = new HashMap<Date, Date>();
			Map<Date, List<OrderFood>> mFoods = new HashMap<Date, List<OrderFood>>();
			Map<Date, List<OrderFood>> sFoods = new HashMap<Date, List<OrderFood>>();
			
			List<Order> upgradeOrders = new ArrayList<Order>();
			List<Long> upgradeOrderIds = new ArrayList<Long>();
			List<OrderFood> upgradeFoods = new ArrayList<OrderFood>();
			List<String> upgradeOrderFoodIds = new ArrayList<String>();
			
			// 1. group by booktime
			for(Order otmp : orders){
				if(otmp.getUserid() > 0 && otmp.getZoneid() > 0 && otmp.getZoneid() != 59){
					Date btmp = otmp.getBooktime();
	
					// consume time;
					if(otmp.getConsumetime() != null)
						consumeTime.put(btmp, otmp.getConsumetime());
					
					// group
					List<Order> ostmp = groupOrders.get(btmp);
					if(ostmp == null){
						ostmp = new ArrayList<Order>();
						groupOrders.put(btmp, ostmp);
					}
					ostmp.add(otmp);
					
					// user
					User user = new User();
					user.setUserId(otmp.getUserid());
					user.setUserName(otmp.getUsername());
					user.setZoneId(otmp.getZoneid());
					users.add(user);
					
					// min order time
					Date minOrderTmp = minOrderTime.get(btmp);
					if(minOrderTmp == null || minOrderTmp.after(otmp.getOrdertime()))
						minOrderTime.put(btmp, otmp.getOrdertime());
					
					// foods
					List<OrderFood> tmpFoods = otmp.getOrderFood();
					if(tmpFoods != null && tmpFoods.size() > 0)
						for(OrderFood oFood : tmpFoods)
							if(oFood.getFoodtype() == Food.FOOD_TYPE_MAIN){
								List<OrderFood> orderFoodsTmp = mFoods.get(btmp);
								if(orderFoodsTmp == null){
									orderFoodsTmp = new ArrayList<OrderFood>();
									mFoods.put(btmp, orderFoodsTmp);
								}
								orderFoodsTmp.add(oFood);
							} else if(oFood.getFoodtype() == Food.FOOD_TYPE_DRINK){
								List<OrderFood> orderFoodsTmp = sFoods.get(btmp);
								if(orderFoodsTmp == null){
									orderFoodsTmp = new ArrayList<OrderFood>();
									sFoods.put(btmp, orderFoodsTmp);
								}
								orderFoodsTmp.add(oFood);
							}
				}
			}
			logger.debug("groupOrders >> " + groupOrders);
			logger.debug("users >> " + users);
			logger.info("minOrderTime >> " + minOrderTime);
			logger.info("consumeTime >> " + consumeTime);
			logger.debug("mFoods >> " + mFoods);
			logger.debug("sFoods >> " + sFoods);
			
			long start2 = System.currentTimeMillis();
			logger.info(">> group used " + (start2 - start));
			
			Random random = new Random();
			Date now = new Date();
			
			// load from order_upgrade
			Map<Date, List<Order>> upOrders = orderDao.getUpOrderByQuery(order, dateF, dateT);
			List<Order> upOrderFoodsTmp = new ArrayList<Order>();
			if(upOrders != null){
				for(List<Order> otmps : upOrders.values())
					upOrderFoodsTmp.addAll(otmps);
				for(Order o : upOrderFoodsTmp){
					List<OrderFood> or = orderFoodDao.getOrderFoodsByOrderid(o.getOrderid());
					o.setOrderFood(or);
					orderfoodMap.put(o.getOrderid(), or);
				}
			}
			
			long start3 = System.currentTimeMillis();
			logger.info(">> getUpOrderByQuery used " + (start2 - start2));
			logger.debug("getUpOrderByQuery >> " + upOrders);
			
			for(Date bookDate : groupOrders.keySet()){
				List<Order> orderUpTmp = upOrders.get(bookDate);
				if(orderUpTmp != null)
					orders.addAll(orderUpTmp);
				
				if(orderUpTmp == null || bookDate.after(now)){
					// 2. get real count
					int realCnt = groupOrders.get(bookDate) == null ? 0 : groupOrders.get(bookDate).size();
	
					Date startTime = minOrderTime.get(bookDate);
					long endTime = bookDate.getTime();
					// current date gap
					if(bookDate.after(now))
						endTime = now.getTime();
					int gap = new Long(endTime - startTime.getTime()).intValue();
					
					// 3. calc fake count
					// current fake cnt
					int min = 100;
					int max = 100;
					if(bookDate.after(now)){
						min = new Long(min * (endTime - startTime.getTime()) / (bookDate.getTime() - startTime.getTime())).intValue();
						max = min;
					}
					int fakeCnt = min - realCnt - (orderUpTmp == null ? 0 : orderUpTmp.size());
					fakeCnt += random.nextInt(max);
					
					Date cTime = consumeTime.get(bookDate);
					if(cTime == null)
						cTime = new Date(bookDate.getTime() + random.nextInt(86400000));
					
					for(int i = 0; i < fakeCnt; i ++){
						Order orderTmp = new Order();
						orders.add(orderTmp);
						upgradeOrders.add(orderTmp);
						
						// 5. random userid, username & zoneid
						int ranUser = random.nextInt(users.size());
						User oUser = users.get(ranUser);
						orderTmp.setUserid(oUser.getUserId());
						orderTmp.setUsername(oUser.getUserName());
						orderTmp.setZoneid(oUser.getZoneId());
						
						// 6. random payway
						int ranPay = random.nextInt(PayMode.MODE_ALI_APP);
						orderTmp.setPayway(ranPay);
						
						// 7. random ordertime
						Date oTime = new Date(startTime.getTime() + random.nextInt(gap));
						orderTmp.setOrdertime(oTime);
						
						// 4. gen orderid
						String date = DateFormatUtils.format(oTime, "yyyyMMdd");
						long id = orderDao.genTodayId(date, new Long(oUser.getZoneId()).intValue());
						String result = StringUtils.substring(date, 2);
						result += StringUtils.leftPad(new Long(oUser.getZoneId()).toString(), 2, "0");
						result += StringUtils.leftPad(new Long(id).toString(), 4, "0");
						orderTmp.setOrderid(Long.parseLong(result));
						upgradeOrderIds.add(orderTmp.getOrderid());
						
						// 8. set booktime
						orderTmp.setBooktime(bookDate);
						
						// 9. set state
						if(bookDate.before(now) && !DateUtils.isSameDay(bookDate, now)){
							orderTmp.setState(Order.STATE_TOKEN);
							orderTmp.setConsumetime(cTime);
						} else{
							if(ranPay == PayMode.MODE_OFFLINE)
								orderTmp.setState(Order.STATE_UNPAID);
							else
								orderTmp.setState(Order.STATE_PAID);
						}
						
						// 10. randome foods & saleprice
						List<OrderFood> foodsTmp = new ArrayList<OrderFood>();
						int mIndex = random.nextInt(2);
						if(mIndex == 1){
							List<OrderFood> foodsMain = mFoods.get(bookDate);
							OrderFood ofTmp = foodsMain.get(random.nextInt(foodsMain.size()));
							OrderFood ofMain = new OrderFood();
							ofMain.setFoodid(ofTmp.getFoodid());
							ofMain.setFoodname(ofTmp.getFoodname());
							ofMain.setFoodtype(ofTmp.getFoodtype());
							ofMain.setOrderid(orderTmp.getOrderid());
							ofMain.setPrice(ofTmp.getPrice());
							ofMain.setUserid(oUser.getUserId());
							foodsTmp.add(ofMain);
							upgradeOrderFoodIds.add(orderTmp.getOrderid() + "-" + ofMain.getFoodid());
							
							int sIndex = random.nextInt(2);
							if(sIndex == 1){
								List<OrderFood> foodsSec = sFoods.get(bookDate);
								ofTmp = foodsSec.get(random.nextInt(foodsSec.size()));
								OrderFood ofSec = new OrderFood();
								ofSec.setFoodid(ofTmp.getFoodid());
								ofSec.setFoodname(ofTmp.getFoodname());
								ofSec.setFoodtype(ofTmp.getFoodtype());
								ofSec.setOrderid(orderTmp.getOrderid());
								ofSec.setPrice(ofTmp.getPrice());
								ofSec.setUserid(oUser.getUserId());
								foodsTmp.add(ofSec);
								upgradeOrderFoodIds.add(orderTmp.getOrderid() + "-" + ofSec.getFoodid());
							}
						} else{
							List<OrderFood> foodsSec = sFoods.get(bookDate);
							OrderFood ofTmp = foodsSec.get(random.nextInt(foodsSec.size()));
							OrderFood ofSec = new OrderFood();
							ofSec.setFoodid(ofTmp.getFoodid());
							ofSec.setFoodname(ofTmp.getFoodname());
							ofSec.setFoodtype(ofTmp.getFoodtype());
							ofSec.setOrderid(orderTmp.getOrderid());
							ofSec.setPrice(ofTmp.getPrice());
							ofSec.setUserid(oUser.getUserId());
							foodsTmp.add(ofSec);
							upgradeOrderFoodIds.add(orderTmp.getOrderid() + "-" + ofSec.getFoodid());
						}
						orderfoodMap.put(orderTmp.getOrderid(), foodsTmp);
						upgradeFoods.addAll(foodsTmp);
						
						// 11. price
						FoodPrice price = getFoodPrice(foodsTmp.get(0), foodsTmp.size() > 1 ? foodsTmp.get(1) : null);
						if(price.getPromotionPrice() > 0)
							orderTmp.setPrice(price.getPromotionPrice());
						else if(price.getComboPrice() > 0)
							orderTmp.setPrice(price.getComboPrice());
						else
							orderTmp.setPrice(price.getSalePrice());
						
						orderTmp.setOrderFood(foodsTmp);
					}
				}
			}
			
			long start4 = System.currentTimeMillis();
			logger.info(">> auto genOrders used " + (start4 - start3));

			logger.info("upgradeOrders >> " + upgradeOrderIds);
			logger.info("upgradeFoods >> " + upgradeOrderFoodIds);
			if(upgradeOrders.size() > 0){
				// save to order_upgrade & order_food
				orderDao.addFoods(upgradeFoods);
				orderDao.addOrders(upgradeOrders);
			}
			
			long start5 = System.currentTimeMillis();
			logger.info(">> auto genOrders used " + (start5 - start4));
			
			logger.info(">> auto total used " + (start5 - start));
		}
		
		if(orders!=null && orders.size()>0)
		{
			List<Long> zoneids = new ArrayList<Long>();
			List<Long> orderids = new ArrayList<Long>();
			Map<Long,String> zoneMap = new HashMap<Long, String>();
			
			for(Order o : orders)
			{
				zoneids.add(o.getZoneid());
				orderids.add(o.getOrderid());
//				List<OrderFood> or = orderFoodDao.getOrderFoodsByOrderid(o.getOrderid());
//				orderfoodMap.put(o.getOrderid(), or);
			}
			
			List<Zone> zones = zoneDao.findZoneByIds(zoneids);
			if(zones!=null && zones.size()>0)
			{
				for(Zone zone : zones)
				{
					zoneMap.put(zone.getId(), zone.getName());
				}
			}
			
			
			//make up
			for(Order o : orders)
			{
				OrderQueryRes re = new OrderQueryRes();
				re.setPrice(o.getPrice());
				re.setId(o.getOrderid());
				if(o.getOrdertime() != null)
					re.setOrderdate(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(o.getOrdertime()));
				re.setPayway(transPayway(o.getPayway()));
				if(o.getConsumetime() != null)
					re.setPickdate(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(o.getConsumetime()));
				re.setPickname(zoneMap.get(o.getZoneid()));
				re.setState(transState(o.getState()));
				re.setStateInt(o.getState());
				re.setUserid(o.getUserid());
				re.setUsername(o.getUsername());
				re.setBookdate(o.getBooktime());
				List<OrderFood> fs = orderfoodMap.get(o.getOrderid());
				boolean f = false;
				if(query.getMainfoodid() != -1){
					if(fs != null && fs.size() > 0)
						for(OrderFood of : fs)
							if(of.getFoodtype() == Food.FOOD_TYPE_MAIN && of.getFoodid() == query.getMainfoodid()){
								f = true;
								break;
							}
				} else
					f = true;
				if(f)
					re.setMainfood(transfood(orderfoodMap.get(o.getOrderid()),Food.FOOD_TYPE_MAIN));
				else
					continue;
				f = false;
				if(query.getDrinkfoodid() != -1){
					if(fs != null && fs.size() > 0)
						for(OrderFood of : fs)
							if(of.getFoodtype() == Food.FOOD_TYPE_DRINK && of.getFoodid() == query.getDrinkfoodid()){
								f = true;
								break;
							}
				} else
					f = true;
				if(f)
					re.setDrinkfood(transfood(orderfoodMap.get(o.getOrderid()),Food.FOOD_TYPE_DRINK));
				else
					continue;
				res.add(re);
			}
		}
		return res;
	}

	public FoodPrice getFoodPrice(OrderFood food1, OrderFood food2) {
		FoodPrice price = new FoodPrice();
		
		if(food1 != null && food2 != null && food1.getFoodtype() == food2.getFoodtype())
			return price;
		
		if(food1 != null || food2 != null){
			// single price
			double salePrice = 0;
			if(food1 != null)
				salePrice += food1.getPrice();
			if(food2 != null)
				salePrice += food2.getPrice();
			price.setSalePrice(salePrice);
			
			// combo price
			if(food1 != null && food2 != null)
				price.setComboPrice(price.getSalePrice() - _getComboMargin());
		}
		
		return price;
	}

	private int _getComboMargin() {
		return 1;
	}

	private String transfood(List<OrderFood> orderfoods,int type)
	{
		String res = "";
		
		if(orderfoods==null || orderfoods.size()==0)
			return res;
		for(OrderFood food : orderfoods)
		{
			if(food.getFoodtype()==type)
			{
				res += food.getFoodname()+",";
			}
		}
		if(res!=null && !"".equals(res))
			return res.substring(0,res.length()-1);
		return res;
	}
	
	private String transState(int state)
	{
		switch(state)
		{
		case 0:
			return "未领取未支付";
		case 1:
			return "未领取已支付";
		case 2:
			return "已领取";
		case 3:
			return "已支付过期未取";
		case 4:
			return "未支付过期未取";
		case 5:
			return "已取消";
		}
		return "";
	}
	
	private String transPayway(int payway)
	{
		switch(payway)
		{
		case 0:
			return "线下付费";
		case 1:
			return "余额付费";
		case 2:
			return "支付宝网页在线支付";
		case 3:
			return "支付宝客户端";
		}
		return "";
	}
	
	public List<Order> getActiveOrdersByZoneId(long zoneId){
		List<Order> result = new ArrayList<Order>();
		List<Order> orders = orderDao.getActiveOrdersByZoneId(zoneId);
		
		if(orders != null && orders.size() > 0){
			Set<Long> orderids = new HashSet<Long>();
			for(Order order : orders)
				orderids.add(order.getOrderid());
			
			if(orderids.size() > 0){
				Map<Long, List<OrderFood>> foods = orderDao.getOrderFoodByOrderIds(orderids);

				if(foods != null && foods.size() > 0)
					for(Order order: orders)
						if(foods.containsKey(order.getOrderid())){
							List<OrderFood> tmp = foods.get(order.getOrderid());
							if(tmp != null && tmp.size() > 0){
								order.setOrderFood(tmp);
								result.add(order);
							}
						}
			}
		}
		
		return result;
	}

	public Order getOrderById(long orderid){
		return orderDao.getOrderById(orderid);
	}

	@Override
	public boolean consumeOrder(Order order) {
		return orderDao.updateOrderState(Order.STATE_TOKEN, order);
	}

	public boolean isUpgrade() {
		return upgrade;
	}

	public void setUpgrade(boolean upgrade) {
		this.upgrade = upgrade;
	}
}
